<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button id="btn">登录</button>
</body>
<script>
  class Login {
    createLayout() {
      var oDiv = document.createElement('div')
      var num = arguments[0]
      oDiv.innerHTML = `我是${num}`
      document.body.appendChild(oDiv)
      oDiv.style.display = 'none'
      return oDiv
    }
  }

  class Single {
    getSingle(fn) {
      var result;
      return function (str) {
        return result || (result = fn.call(this, str))
      }
    }
  }

  var oBtn = document.getElementById('btn')
  var single = new Single()
  var login = new Login()

  // 由于闭包，createLoginLayer对result的引用，所以当single.getSingle函数执行完之后，内存中并不会销毁result。

  // 当第二次以后点击按钮，根据createLoginLayer函数的作用域链中已经包含了result，所以直接返回result

  // 讲获取单例和创建登录框的方法解耦，符合开放封闭原则
  var createLoginLayer = single.getSingle(login.createLayout)
  oBtn.onclick = function () {
    var layout = createLoginLayer('登录框2')
    layout.style.display = 'block'
  }

  // 单例模式——一个构造函数只有一个实例

  // 闭包储存

  const Person = (function () {
    // 构造函数
    function Person(name, age, sex) {
      this.name = name
      this.age = age
      this.sex = sex
    }
    Person.prototype.say = function () {
      console.log(`我叫${this.name},年龄是${this.age},性别${this.sex}`);
    }
    // instance用于作实例的载体
    let instance = null

    // 返回函数形成闭包
    return function (...args) {
      // instance不存在时赋值一个实例 
      // instance存在时返回的是之前的实例
      if (!instance) instance = new Person(...args)
      return instance
    }

  })()

  const p1 = Person('张三', 28, '男')
  console.log(p1);
  const p2 = Person('李四', 30, '男') // 赋值
  console.log(p1 === p2);
  p1.say()

  //安全模式创建的工厂方法函数
  let UserFactory = function (role) {
    if (this instanceof UserFactory) {
      // console.log(this[role]);
      var s = new this[role]();
      console.log(s);
      return s;
    } else {
      return new UserFactory(role);
    }
  }

  //工厂方法函数的原型中设置所有对象的构造函数
  UserFactory.prototype = {
    SuperAdmin: function () {
      this.name = "超级管理员",
        this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
    },
    Admin: function () {
      this.name = "管理员",
        this.viewPage = ['首页', '通讯录', '发现页', '应用数据']
    },
    NormalUser: function () {
      this.name = '普通用户',
        this.viewPage = ['首页', '通讯录', '发现页']
    }
  }
  UserFactory.prototype.constructor = UserFactory
  //调用
  let superAdmin = UserFactory('SuperAdmin');
  let admin = UserFactory('Admin')
  let normalUser = UserFactory('NormalUser')

  // console.log(superAdmin);

  // 代理模式——缓存代理
  var add = function () {
    var sum = 0;
    for (var i = 0, l = arguments.length; i < l; i++) {
      sum += arguments[i];
    }
    return sum;
  };
  var proxyAdd = (function () {
    var cache = {}; //缓存运算结果的缓存对象
    return function () {
      var args = Array.prototype.join.call(arguments);//把参数用逗号组成一个字符串作为“键”
      if (cache.hasOwnProperty(args)) {//等价 args in cache
        console.log('使用缓存结果');
        return cache[args];//直接使用缓存对象的“值”
      }
      console.log('计算结果');
      return cache[args] = add.apply(this, [...arguments]);//使用本体函数计算结果并加入缓存
    }
  })();
  console.log(proxyAdd(1, 2, 3, 4, 5)); //15
  console.log(proxyAdd(1, 2, 3, 4, 5)); //15
  console.log(proxyAdd(1, 2, 3, 4, 5)); //15
</script>
</body>

</html>